use std::ops::Add;
use std::str::Chars;
use std::time;
use std::time::Duration;

use bytestring::ByteString;

use crate::{Entry, EntryBuilder, LResult};
use crate::error::Error;
use crate::value::FieldValue;

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
enum ParseState {
    Measurement,
    Tag,
    Field,
    Ts,
    // end of current state, should switch to next state
    Next,
    // special state, continue parsing
    Continue,
}

pub(crate) fn parse_entry(s: &str) -> LResult<Entry> {
    let mut state = ParseState::Measurement;
    let mut idx = 0;
    let mut builder = EntryBuilder::default();
    let mut it = s.chars();
    // parse begin
    let measurement = parse_str(&mut idx, s, &mut it, &mut state, |c| {
        if c == ',' {
            ParseState::Tag
        } else if c == ' ' {
            ParseState::Field
        } else {
            ParseState::Continue
        }
    })?;
    builder = builder.measurement(measurement.into());
    while state == ParseState::Tag {
        let token_key = parse_str(&mut idx, s, &mut it, &mut state, |c| {
            if c == '=' {
                ParseState::Next
            } else if c == ' ' {
                ParseState::Field
            } else {
                ParseState::Continue
            }
        })?;
        let token_value = parse_str(&mut idx, s, &mut it, &mut state, |c| {
            if c == ',' {
                ParseState::Tag
            } else if c == ' ' {
                ParseState::Field
            } else {
                ParseState::Continue
            }
        })?;
        builder = builder.add_tag(token_key.try_into()?, token_value.try_into()?);
    }
    if state != ParseState::Field {
        return Err(Error::EntryLineProtocolErr("Illegal line protocol, Missing FieldValue".into()));
    }
    while state == ParseState::Field {
        let token_key = parse_str(&mut idx, s, &mut it, &mut state, |c| {
            if c == '=' {
                ParseState::Next
            } else if c == ' ' {
                ParseState::Ts
            } else {
                ParseState::Continue
            }
        })?;
        let value = parse_field_value(&mut idx, s, &mut it, &mut state)?;
        builder = builder.add_field(token_key.try_into()?, value);
    }
    // parse ts
    if idx >= s.len() {
        return builder.build();
    }
    let ts_s = parse_str(&mut idx, s, &mut it, &mut state, |c| {
        if c == ' ' {
            ParseState::Next
        } else {
            ParseState::Continue
        }
    })?;
    if ts_s.len() < 9 {
        return Err(Error::EntryLineProtocolErr(format!("Illegal Ts: {}", ts_s)));
    }
    let ts_seconds_s = &ts_s[0..ts_s.len() - 9];
    let ts_nano_s = &ts_s[ts_s.len() - 9..ts_s.len()];
    let ts_seconds = ts_seconds_s.parse::<u64>().map_err(|_| {
        Error::EntryLineProtocolErr(format!("Illegal Ts: {}", ts_s))
    })?;
    let ts_nano = ts_nano_s.parse::<u32>().map_err(|_| {
        Error::EntryLineProtocolErr(format!("Illegal Ts: {}", ts_s))
    })?;
    let ts = time::UNIX_EPOCH.add(Duration::new(ts_seconds, ts_nano));
    builder.ts(ts).build()
}

fn parse_str<'a, SF: FnMut(char) -> ParseState>(idx: &mut usize, s: &'a str, it: &mut Chars<'a>, state: &mut ParseState, mut sf: SF) -> LResult<&'a str> {
    let si = *idx;
    let mut len = 0;
    for c in it {
        let ns = sf(c);
        if ns != ParseState::Continue {
            // finish, capture str and jump to next state
            if len == 0 {
                return Err(Error::EntryLineProtocolErr(format!("missing {}", state.state_name())));
            }
            *idx = si + len + c.len_utf8();
            if ns != ParseState::Next {
                *state = ns;
            }
            return Ok(&s[si..si + len]);
        }
        len += c.len_utf8();
    }
    *idx = si + len;
    Ok(&s[si..si + len])
}

fn parse_field_value<'a>(idx: &mut usize, s: &str, it: &mut Chars<'a>, state: &mut ParseState) -> LResult<FieldValue> {
    let mut has_begin_quote = false;
    let mut has_dot = false;
    let mut i = 0;
    let si = *idx;
    let mut ei = si;
    for c in it {
        if i == 0 && c == '"' {
            i += 1;
            ei += c.len_utf8();
            has_begin_quote = true;
            continue;
        }
        if c == '"' {
            // TODO End Quote
        } else if c == ' ' {
            // end of field value
            *state = ParseState::Ts;
            *idx = ei + c.len_utf8();
            return parse_field_value0(s, si, ei, has_dot, has_begin_quote);
        } else if c == ',' {
            *idx = ei + c.len_utf8();
            return parse_field_value0(s, si, ei, has_dot, has_begin_quote);
        } else if c == '.' {
            if has_dot {
                return Err(Error::EntryLineProtocolErr("Illegal Field Value".to_string()));
            } else {
                has_dot = true;
            }
        }
        ei += c.len_utf8();
    }
    if ei > 0 {
        *idx = ei;
        *state = ParseState::Next;
        parse_field_value0(s, si, ei, has_dot, has_begin_quote)
    } else {
        Err(Error::EntryLineProtocolErr(format!("missing {}", state.state_name())))
    }
}

fn parse_field_value0(s: &str, si: usize, ei: usize, has_dot: bool, has_quote: bool) -> LResult<FieldValue> {
    let s0 = if has_quote {
        &s[si + 1..ei - 1]
    } else {
        &s[si..ei]
    };
    let v1 = if has_quote {
        FieldValue::String(ByteString::from(s0))
    } else if has_dot {
        let v0 = s0.parse::<f64>().map_err(|_| {
            Error::EntryLineProtocolErr(format!("Illegal Field Value: {}", s0))
        })?;
        FieldValue::F64(v0)
    } else {
        let v0 = s0.parse::<i64>().map_err(|_| {
            Error::EntryLineProtocolErr(format!("Illegal Field Value: {}", s0))
        })?;
        FieldValue::I64(v0)
    };
    Ok(v1)
}

impl ParseState {
    fn state_name(self) -> &'static str {
        match self {
            ParseState::Measurement => "Measurement",
            ParseState::Tag => "Tag",
            ParseState::Field => "Field",
            ParseState::Ts => "Ts",
            _ => unreachable!()
        }
    }
}

#[cfg(test)]
mod test {
    use std::mem::size_of_val;

    use bytestring::ByteString;
    use log::debug;

    use crate::{Entry, LResult};
    use crate::entry_parser::{parse_entry, parse_field_value, ParseState};
    use crate::value::FieldValue;

    #[test]
    fn test() {
        let s = "aoi爱啊急啊看112";
        for c in s.chars() {
            debug!(target: "common", "c: {}, len: {}, size_of: {}", c, c.len_utf8(), size_of_val(&c));
        }
        let ss = &s[0..(3 + 3 * 2)];
        debug!(target: "common", "ss: {}", ss);
    }

    #[test]
    fn test_parse_field_value() -> LResult<()> {
        assert_field_value("10293", FieldValue::I64(10293))?;
        assert_field_value("1", FieldValue::I64(1))?;
        assert_field_value("-1029", FieldValue::I64(-1029))?;
        assert_field_value("12.2", FieldValue::F64(12.2))?;
        assert_field_value("-13.", FieldValue::F64(-13.0))?;
        assert_field_value("\"\"", FieldValue::String(ByteString::from("")))?;
        assert_field_value("\"abc\"", FieldValue::String(ByteString::from("abc")))?;
        assert_field_value("\"我是中文\"", FieldValue::String(ByteString::from("我是中文")))?;
        assert_field_value("\"12a我a是v中e文eew\"", FieldValue::String(ByteString::from("12a我a是v中e文eew")))?;
        Ok(())
    }

    #[test]
    fn test_line_protocol() -> LResult<()> {
        // 0
        assert_line_protocol("a k=1", |e| {
            assert_eq!("a", e.measurement());
            let v = e.get_field("k").unwrap().unwrap();
            assert_eq!(&FieldValue::I64(1), v);
            assert_eq!(0, e.tags_count());
            assert!(e.ts_nano() > 0);
        })?;
        // 1
        assert_line_protocol("a k=1 1713440942512802815", |e| {
            assert_eq!("a", e.measurement());
            let v = e.get_field("k").unwrap().unwrap();
            assert_eq!(&FieldValue::I64(1), v);
            assert_eq!(0, e.tags_count());
            assert_eq!(1713440942512802815, e.ts_nano());
        })?;
        // 2
        assert_line_protocol("a k=1,g=\"a\" 1713440942512802815", |e| {
            assert_eq!("a", e.measurement());
            let v = e.get_field("k").unwrap().unwrap();
            assert_eq!(&FieldValue::I64(1), v);
            let v = e.get_field("g").unwrap().unwrap();
            assert_eq!(&FieldValue::String("a".into()), v);
            assert_eq!(0, e.tags_count());
            assert_eq!(1713440942512802815, e.ts_nano());
        })?;
        // 3
        assert_line_protocol("a,a=1 k=1 1713440942512802815", |e| {
            assert_eq!("a", e.measurement());
            let v = &*e.get_tag("a").unwrap().unwrap().get_string();
            assert_eq!("1", v);
            let v = e.get_field("k").unwrap().unwrap();
            assert_eq!(&FieldValue::I64(1), v);
            assert_eq!(1713440942512802815, e.ts_nano());
        })?;
        // 4
        assert_line_protocol("a,a=1,b=2 k=1 1713440942512802815", |e| {
            assert_eq!("a", e.measurement());
            let v = &*e.get_tag("a").unwrap().unwrap().get_string();
            assert_eq!("1", v);
            let v = &*e.get_tag("b").unwrap().unwrap().get_string();
            assert_eq!("2", v);
            let v = e.get_field("k").unwrap().unwrap();
            assert_eq!(&FieldValue::I64(1), v);
            assert_eq!(1713440942512802815, e.ts_nano());
        })?;
        // 4
        assert_line_protocol("a,a=1,b=2,c=ccc k=1,a=2.32,b=\"哈哈\" 1713440942512802815", |e| {
            assert_eq!("a", e.measurement());
            let v = &*e.get_tag("a").unwrap().unwrap().get_string();
            assert_eq!("1", v);
            let v = &*e.get_tag("b").unwrap().unwrap().get_string();
            assert_eq!("2", v);
            let v = &*e.get_tag("c").unwrap().unwrap().get_string();
            assert_eq!("ccc", v);
            let v = e.get_field("k").unwrap().unwrap();
            assert_eq!(&FieldValue::I64(1), v);
            let v = e.get_field("a").unwrap().unwrap();
            assert_eq!(&FieldValue::F64(2.32), v);
            let v = e.get_field("b").unwrap().unwrap();
            assert_eq!(&FieldValue::String("哈哈".into()), v);
            assert_eq!(1713440942512802815, e.ts_nano());
        })?;
        assert_line_protocol("test,tag_a=abc,tag_b=aaa field_a=123,field_b=\"aaa\"", |e| {
            assert_eq!("test", &e.measurement);
            assert_eq!("abc", e.get_tag("tag_a").unwrap().unwrap().to_string());
            assert_eq!("aaa", e.get_tag("tag_b").unwrap().unwrap().to_string());
            assert_eq!(&FieldValue::I64(123), e.get_field("field_a").unwrap().unwrap());
            assert_eq!(&FieldValue::String("aaa".into()), e.get_field("field_b").unwrap().unwrap());
        })?;
        Ok(())
    }

    fn assert_field_value(s: &str, fv: FieldValue) -> LResult<()> {
        let mut idx = 0;
        let mut it = s.chars();
        let mut state = ParseState::Field;
        let v0 = parse_field_value(&mut idx, s, &mut it, &mut state)?;
        assert_eq!(fv, v0);
        // assert idx point to the next un-parse char
        assert_eq!(s.len(), idx, "{}", s);
        Ok(())
    }

    fn assert_line_protocol<F: FnOnce(Entry) -> ()>(s: &str, f: F) -> LResult<()> {
        let e = parse_entry(s)?;
        debug!(target: "common", "entry: {}", &e);
        f(e);
        Ok(())
    }
}